﻿using mozhi.smarterp.Business.Enums;
using mozhi.smarterp.Business.Purchases.Repositories;
using mozhi.smarterp.Business.Purchases.Services;
using Mozhi.Abp.Domain.AuthorCenter;
using Mozhi.Abp.Domain.BasicData;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;

namespace mozhi.smarterp.Business.Purchases.Requisition
{
    public class PurchaseRequisitionAppService : BusinessAppService, IPurchaseRequisitionAppService
    {
        private IPurchaseRequisitionRepository _repository;
        private PurchaseRequisitionManager _manager;
        private PurchaseRequisitionOrderManager _requisitionOrderManager;
        private IPurchaseOrderRepository _orderRepository;
        private IPurchaseRequisitionOrderRepository _requisitionOrderRepository;
        private IProductRepository _productRepository;
        private IDepartmentRepository _departmentRepository;
        private IEmployeeRepository _employeeRepository;
        private ITaxRateRepository _taxRateRepository;
        private ICurrencyRepository _currencyRepository;
        private IUnitRepository _unitRepository;
        private ISupplierRepository _supplierRepository;

        public PurchaseRequisitionAppService(IPurchaseRequisitionRepository repository , PurchaseRequisitionManager manager, PurchaseRequisitionOrderManager requisitionOrderManager, 
            IPurchaseOrderRepository orderRepository , IPurchaseRequisitionOrderRepository requisitionOrderRepository,ICurrencyRepository currencyRepository , IProductRepository productRepository,
            IDepartmentRepository departmentRepository , IEmployeeRepository employeeRepository , ITaxRateRepository taxRateRepository , IUnitRepository unitRepository ,
            ISupplierRepository supplierRepository)
        {
            _repository = repository;
            _manager = manager;
            _requisitionOrderManager = requisitionOrderManager;
            _orderRepository = orderRepository;
            _requisitionOrderRepository = requisitionOrderRepository;
            _currencyRepository = currencyRepository;
            _unitRepository = unitRepository;
            _supplierRepository = supplierRepository;
            _currencyRepository = currencyRepository;
            _productRepository = productRepository;
            _departmentRepository = departmentRepository;
            _employeeRepository = employeeRepository;
            _taxRateRepository = taxRateRepository;
        }
        

        public async Task<PurchaseRequisitionDto> GetAsync(Guid id)
        {
            var requisition = await _repository.GetAsync(id);

            var result = ObjectMapper.Map<PurchaseRequisition, PurchaseRequisitionDto>(requisition);

            await LoadOtherInfoAsync(new List<PurchaseRequisitionDto> { result });

            return result;
        }

        public async Task<PagedResultDto<PurchaseRequisitionDto>> GetListAsync(PurchaseRequisitonResultRequestDto filter)
        {
            var requisitions = await _repository.GetPagedListAsync(filter.Number ,filter.StartDate , filter.EndDate , filter.Status ,filter.SkipCount, filter.MaxResultCount, filter.Sorting, true);

            var count = await _repository.GetCountAsync(filter.Number, filter.StartDate, filter.EndDate, filter.Status);

            var result = new PagedResultDto<PurchaseRequisitionDto>()
            {
                Items = ObjectMapper.Map<List<PurchaseRequisition>, List<PurchaseRequisitionDto>>(requisitions),
                TotalCount = count
            };

            await LoadOtherInfoAsync(result.Items.ToList());

            return result;
        }

        public async Task<PurchaseRequisitionDto> CreateAsync(PurchaseRequisitionCreateDto input)
        {
            var requisition = await _manager.GetAysnc(null, input.DepartmentId, input.Date, input.Comment, input.ProductType, input.CurrencyId, input.ExchangeRate,
                input.IncludeTax, input.ApplicantId);

            input.Items = input.Items ?? new List<PurchaseRequisitionItemCreateDto>();

            var requisitionItems = new List<PurchaseRequisitionItem>();

            foreach (var item in input.Items)
            {
                var requisitionItem = await _manager.GetItemsAsync(null, item.ProductId, item.RequisitionUnitId, item.RequisitionQuantity, item.RequisitionQuantity, item.Price ?? 0, item.RequiredDate, item.TaxRateId, item.SuggestedSupplierId, item.Remark);

                requisitionItems.Add(requisitionItem);
            }

            requisition.ChangeItems(requisitionItems);


            requisition = await _repository.InsertAsync(requisition);

            var result =  ObjectMapper.Map<PurchaseRequisition, PurchaseRequisitionDto>(requisition);

            await LoadOtherInfoAsync(new List<PurchaseRequisitionDto> { result });

            return result;

        }

        public async Task<PurchaseRequisitionDto> UpdateAsync(PurchaseRequisitionUpdateDto input)
        {
            var requisition = await _manager.GetAysnc(input.Id, input.DepartmentId, input.Date, input.Comment, input.ProductType, input.CurrencyId, input.ExchangeRate,
                input.IncludeTax, input.ApplicantId);

            input.Items = input.Items ?? new List<PurchaseRequisitionItemUpdateDto>();

            var requisitionItems = new List<PurchaseRequisitionItem>();

            foreach (var item in input.Items)
            {
                var requisitionItem = await _manager.GetItemsAsync(item.Id, item.ProductId, item.RequisitionUnitId, item.RequisitionQuantity, item.ApprovedQuantity, item.Price ?? 0, item.RequiredDate, item.TaxRateId, item.SuggestedSupplierId, item.Remark);

                requisitionItems.Add(requisitionItem);
            }

            requisition.ChangeItems(requisitionItems);

            requisition = await _repository.UpdateAsync(requisition);

            var result = ObjectMapper.Map<PurchaseRequisition, PurchaseRequisitionDto>(requisition);

            await LoadOtherInfoAsync(new List<PurchaseRequisitionDto> { result });

            return result;
        }

        public async Task DeleteAsync(Guid id)
        {
            var requisition = await _repository.GetAsync(id, false);

            if (requisition == null)
                throw new UserFriendlyException($"申请单：{id}不存在", "100000");

            if (requisition.Status != DocumentStatusType.Creation)
                throw new UserFriendlyException($"申请单：{requisition.Number}不是创建状态，不允许删除", "100000");

            await _repository.DeleteAsync(id);
        }

        public async Task UpdateStatusAsync(Guid id, DocumentStatusType status)
        {
            var requisition = await _manager.UpdateStatusAsync(id, status);

            await _repository.UpdateAsync(requisition);
        }

        /// <summary>
        /// 下推订单
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<bool> PushDownOrderAsync(Guid id)
        {
            var tuple = await _requisitionOrderManager.GetPurchaseOrdersFromRequisitionAsync(id);

            var orders = tuple.Item1;
            var requisitionOrders = tuple.Item2;

            if (orders == null || orders.Count == 0 || requisitionOrders==null || requisitionOrders.Count==0)
                return false;

            await _orderRepository.InsertManyAsync(orders);
            await _requisitionOrderRepository.InsertManyAsync(requisitionOrders);

            return true;
        }

        #region
        private async Task LoadOtherInfoAsync(List<PurchaseRequisitionDto> requisitions)
        {
            if (requisitions == null || requisitions.Count == 0)
                return;

            foreach (var requisition in requisitions)
            {
                var currency = await _currencyRepository.GetAsync(requisition.CurrencyId);
                var department = requisition.DepartmentId.HasValue ? await _departmentRepository.GetAsync(requisition.DepartmentId.Value) : null;
                var applicant = requisition.ApplicantId.HasValue ? await _employeeRepository.GetAsync(requisition.ApplicantId.Value) : null;

                requisition.CurrencyName = currency?.Name;
                requisition.DepartmentName = department?.Name;
                requisition.ApplicantName = applicant?.Name;

                foreach (var item in requisition.Items)
                {
                    var product = await _productRepository.GetAsync(item.ProductId);
                    var supplier = item.SuggestedSupplierId.HasValue ? await _supplierRepository.GetAsync(item.SuggestedSupplierId.Value) : null;
                    var taxRate = item.TaxRateId.HasValue ? await _taxRateRepository.GetAsync(item.TaxRateId.Value) : null;
                    var requisitionUnit = await _unitRepository.GetAsync(item.RequisitionUnitId);
                    var basicUnit = await _unitRepository.GetAsync(item.BasicUnitId);

                    item.ProductCode = product?.Code;
                    item.ProductName = product?.Name;
                    item.ProductModel = product?.Model;

                    item.SuggestedSupplierName = supplier?.Name;
                    item.TaxRateName = taxRate?.Name;
                    item.RequisitionUnitName = requisitionUnit?.Name;
                    item.BasicUnitName = basicUnit?.Name;
                    
                }
            }
        }

        #endregion
    }
}
